/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package com.icee.myth.gm.client;

import com.google.protobuf.RpcController;
import com.google.protobuf.ServiceException;
import com.googlecode.protobuf.netty.NettyRpcChannel;
import com.googlecode.protobuf.netty.NettyRpcClient;
import com.icee.myth.gm.config.ServerConfig;
import com.icee.myth.gm.player.SandBox;
import com.icee.myth.protobuf.ExternalCommonProtocol.*;
import com.icee.myth.protobuf.RpcServiceProtocol.*;
import com.icee.myth.protobuf.RpcServiceProtocol.ManagerControlService.BlockingInterface;
import com.icee.myth.utils.Consts;
import com.icee.myth.utils.LogConsts;
import com.icee.myth.utils.MLogger;
import com.icee.myth.utils.StackTraceUtil;
import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory;

import java.net.InetSocketAddress;
import java.util.List;
import java.util.concurrent.Executors;

/**
 * 通过RPC调用游戏服务器的函数。
 * @author yangyi
 */
public class GameServerClient {

    public ServerConfig serverConfig;

    public GameServerClient(ServerConfig serverConfig) {
        this.serverConfig = serverConfig;
    }

    public boolean addItem(int cid, int itemId, int itemNum) {
        NettyRpcClient client = new NettyRpcClient(
                new NioClientSocketChannelFactory(
                Executors.newCachedThreadPool(),
                Executors.newCachedThreadPool()));

        NettyRpcChannel channel = null;
        try {
            channel = client.blockingConnect(new InetSocketAddress(serverConfig.host, serverConfig.rpcPort));

            BlockingInterface blockingService = ManagerControlService.newBlockingStub(channel);

            // Get a new RpcController to use for this rpc call
            final RpcController controller = channel.newRpcController();
            GMItemProto.Builder builder = GMItemProto.newBuilder();
            builder.setCid(cid);
            builder.setItemId(itemId);
            builder.setItemNum(itemNum);
            // Create the request

            // Now try a blocking RPC request
            BoolValueProto result = null;
            if (channel.isConnected()) {
                result = blockingService.addItem(controller, builder.build());
            }
            controller.reset();

            if (result != null) {
                return result.getValue();
            }
            
        } catch (ServiceException ex) {
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, "add item error : " + StackTraceUtil.getStackTrace(ex));
        } finally{
            if (channel != null) {
                channel.close();
            }
        }

        return false;
    }

    public boolean removeItem(int cid, int itemId, int itemNum) {
        NettyRpcClient client = new NettyRpcClient(
                new NioClientSocketChannelFactory(
                Executors.newCachedThreadPool(),
                Executors.newCachedThreadPool()));

        NettyRpcChannel channel = null;
        try {
            channel = client.blockingConnect(new InetSocketAddress(serverConfig.host, serverConfig.rpcPort));

            BlockingInterface blockingService = ManagerControlService.newBlockingStub(channel);

            // Get a new RpcController to use for this rpc call
            final RpcController controller = channel.newRpcController();
            GMItemProto.Builder builder = GMItemProto.newBuilder();
            builder.setCid(cid);
            builder.setItemId(itemId);
            builder.setItemNum(itemNum);
            // Create the request

            // Now try a blocking RPC request
            BoolValueProto result = null;
            if (channel.isConnected()) {
                result = blockingService.removeItem(controller, builder.build());
            }
            controller.reset();

            if (result != null) {
                return result.getValue();
            }
        } catch (ServiceException ex) {
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, "remove item error : " + StackTraceUtil.getStackTrace(ex));
        } finally{
            if (channel != null) {
                channel.close();
            }
        }

        return false;
    }

    public boolean addCard(int cid, int cardTypeId, int cardLevel) {
        NettyRpcClient client = new NettyRpcClient(
                new NioClientSocketChannelFactory(
                Executors.newCachedThreadPool(),
                Executors.newCachedThreadPool()));

        NettyRpcChannel channel = null;
        try {
            channel = client.blockingConnect(new InetSocketAddress(serverConfig.host, serverConfig.rpcPort));

            BlockingInterface blockingService = ManagerControlService.newBlockingStub(channel);

            // Get a new RpcController to use for this rpc call
            final RpcController controller = channel.newRpcController();
            GMCardProto.Builder builder = GMCardProto.newBuilder();
            builder.setCid(cid);
            builder.setCardTypeId(cardTypeId);
            builder.setCardLevel(cardLevel);
            // Create the request

            // Now try a blocking RPC request
            BoolValueProto result = null;
            if (channel.isConnected()) {
                result = blockingService.addCard(controller, builder.build());
            }
            controller.reset();

            if (result != null) {
                return result.getValue();
            }

        } catch (ServiceException ex) {
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, "add card error : " + StackTraceUtil.getStackTrace(ex));
        } finally{
            if (channel != null) {
                channel.close();
            }
        }

        return false;
    }

    public boolean removeCard(int cid, int cardInstId) {
        NettyRpcClient client = new NettyRpcClient(
                new NioClientSocketChannelFactory(
                Executors.newCachedThreadPool(),
                Executors.newCachedThreadPool()));

        NettyRpcChannel channel = null;
        try {
            channel = client.blockingConnect(new InetSocketAddress(serverConfig.host, serverConfig.rpcPort));

            BlockingInterface blockingService = ManagerControlService.newBlockingStub(channel);

            // Get a new RpcController to use for this rpc call
            final RpcController controller = channel.newRpcController();
            VariableValueProto.Builder builder = VariableValueProto.newBuilder();
            builder.setId(cid);
            builder.setValue(cardInstId);
            // Create the request

            // Now try a blocking RPC request
            BoolValueProto result = null;
            if (channel.isConnected()) {
                result = blockingService.removeCard(controller, builder.build());
            }
            controller.reset();

            if (result != null) {
                return result.getValue();
            }
        } catch (ServiceException ex) {
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, "remove card error : " + StackTraceUtil.getStackTrace(ex));
        } finally{
            if (channel != null) {
                channel.close();
            }
        }

        return false;
    }

    public boolean changeSandbox(int cid, SandBox sandBox) {
        NettyRpcClient client = new NettyRpcClient(
                new NioClientSocketChannelFactory(
                Executors.newCachedThreadPool(),
                Executors.newCachedThreadPool()));

        NettyRpcChannel channel = null;
        try {
            channel = client.blockingConnect(new InetSocketAddress(serverConfig.host, serverConfig.rpcPort));

            BlockingInterface blockingService = ManagerControlService.newBlockingStub(channel);

            // Get a new RpcController to use for this rpc call
            final RpcController controller = channel.newRpcController();
            GMSandboxProto.Builder builder = GMSandboxProto.newBuilder();
            builder.setCid(cid);
            builder.setSandbox(sandBox.buildSandboxProto());
            // Create the request

            // Now try a blocking RPC request
            BoolValueProto result = null;
            if (channel.isConnected()) {
                result = blockingService.changeSandbox(controller, builder.build());
            }
            controller.reset();

            if (result != null) {
                return result.getValue();
            }
        } catch (ServiceException ex) {
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, "change sandbox error : " + StackTraceUtil.getStackTrace(ex));
        } finally{
            if (channel != null) {
                channel.close();
            }
        }

        return false;
    }

    public boolean forbidLogin(int cid) {
        NettyRpcClient client = new NettyRpcClient(
                new NioClientSocketChannelFactory(
                Executors.newCachedThreadPool(),
                Executors.newCachedThreadPool()));

        // Now try a blocking RPC request
        NettyRpcChannel channel = null;
        try {
            BoolValueProto result = null;
            channel = client.blockingConnect(new InetSocketAddress(serverConfig.host, serverConfig.rpcPort));

            BlockingInterface blockingService = ManagerControlService.newBlockingStub(channel);

            // Get a new RpcController to use for this rpc call
            final RpcController controller = channel.newRpcController();

            // Create the request
            IntValueProto.Builder builder = IntValueProto.newBuilder();
            builder.setValue(cid);
            if (channel.isConnected()) {
                result = blockingService.kickPlayer(controller, builder.build());
            }
            controller.reset();
            if (result != null) {
                return result.getValue();
            }
        } catch (ServiceException ex) {
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, "forbid login error : " + StackTraceUtil.getStackTrace(ex));
        } finally{
            if (channel != null) {
                channel.close();
            }
        }

        return false;
    }

    public boolean forbidTalk(int gameId, int endTime) {
        NettyRpcClient client = new NettyRpcClient(
                new NioClientSocketChannelFactory(
                Executors.newCachedThreadPool(),
                Executors.newCachedThreadPool()));

        NettyRpcChannel channel = null;
        try {
            channel = client.blockingConnect(new InetSocketAddress(serverConfig.host, serverConfig.rpcPort));

            BlockingInterface blockingService = ManagerControlService.newBlockingStub(channel);

            // Get a new RpcController to use for this rpc call
            final RpcController controller = channel.newRpcController();

            // Create the request
            VariableValueProto.Builder builder = VariableValueProto.newBuilder();
            builder.setId(gameId);
            builder.setValue(endTime);
            // Now try a blocking RPC request
            BoolValueProto result = null;
            if (channel.isConnected()) {
                result = blockingService.shutUp(controller, builder.build());
            }
            controller.reset();

            if (result != null) {
                return result.getValue();
            }
        } catch (ServiceException ex) {
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, "forbid talk error : " + StackTraceUtil.getStackTrace(ex));
        } finally{
            if (channel != null) {
                channel.close();
            }
        }

        return false;
    }

    public int getId() {
        return serverConfig.id;
    }

    public String getName() {
        return serverConfig.name;
    }

    public int getPlayerCount() {
        NettyRpcClient client = new NettyRpcClient(
                new NioClientSocketChannelFactory(
                Executors.newCachedThreadPool(),
                Executors.newCachedThreadPool()));
        NettyRpcChannel channel = null;
        try {
            channel = client.blockingConnect(new InetSocketAddress(serverConfig.host, serverConfig.rpcPort));

            BlockingInterface blockingService = ManagerControlService.newBlockingStub(channel);

            // Get a new RpcController to use for this rpc call
            final RpcController controller = channel.newRpcController();

            // Create the request
            VoidProto.Builder voidProtoBuilder = VoidProto.newBuilder();

            // Now try a blocking RPC request
            IntValueProto result = null;
            if (channel.isConnected()) {
                result = blockingService.getOnlinePlayerCount(controller, voidProtoBuilder.build());
                controller.reset();

                if (result != null) {
                    return result.getValue();
                }
            }
        } catch (ServiceException ex) {
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, "Get player num error : " + StackTraceUtil.getStackTrace(ex));
        } finally{
            if (channel != null) {
                channel.close();
            }
        }
        
        return -1;
    }

    public String getServerStatus() {
        throw new UnsupportedOperationException("Not yet implemented");
    }

    public boolean broadcast(String message) {
        NettyRpcClient client = new NettyRpcClient(
                new NioClientSocketChannelFactory(
                Executors.newCachedThreadPool(),
                Executors.newCachedThreadPool()));
        NettyRpcChannel channel = null;
        try {
            channel = client.blockingConnect(new InetSocketAddress(serverConfig.host, serverConfig.rpcPort));

            BlockingInterface blockingService = ManagerControlService.newBlockingStub(channel);

            // Get a new RpcController to use for this rpc call
            final RpcController controller = channel.newRpcController();

            // Create the request
            StringValueProto.Builder builder = StringValueProto.newBuilder();
            builder.setValue(message);
            // Now try a blocking RPC request
            BoolValueProto result = null;
            if (channel.isConnected()) {
                result = blockingService.broadcast(controller, builder.build());
            }
            controller.reset();

            if (result != null) {
                return result.getValue();
            }
        } catch (ServiceException ex) {
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, "broadcast error : " + StackTraceUtil.getStackTrace(ex));
        } finally{
            if (channel != null) {
                channel.close();
            }
        }
        return false;
    }

    public boolean rpcSet(int type, int cid, int num) {
        NettyRpcClient client = new NettyRpcClient(
                new NioClientSocketChannelFactory(
                Executors.newCachedThreadPool(),
                Executors.newCachedThreadPool()));
        NettyRpcChannel channel = null;
        try {
            channel = client.blockingConnect(new InetSocketAddress(serverConfig.host, serverConfig.rpcPort));

            BlockingInterface blockingService = ManagerControlService.newBlockingStub(channel);

            // Get a new RpcController to use for this rpc call
            final RpcController controller = channel.newRpcController();

            // Create the request
            VariableValueProto.Builder builder = VariableValueProto.newBuilder();
            builder.setId(cid);
            builder.setValue(num);
            // Now try a blocking RPC request
            BoolValueProto result = null;
            if (channel.isConnected()) {
                switch (type) {
                    case Consts.RPCSET_SILVER: {
                        result = blockingService.setSilver(controller, builder.build());
                        break;
                    }
                    case Consts.RPCSET_GOLD1: {
                        result = blockingService.setGold1(controller, builder.build());
                        break;
                    }
                    case Consts.RPCSET_GOLD2: {
                        result = blockingService.setGold2(controller, builder.build());
                        break;
                    }
                    case Consts.RPCEDIT_GOLD1: {
                        result = blockingService.editGold1(controller, builder.build());
                        break;
                    }
                    case Consts.RPCEDIT_GOLD2: {
                        result = blockingService.editGold2(controller, builder.build());
                        break;
                    }
                    case Consts.RPCSET_ENERGY: {
                        result = blockingService.setEnergy(controller, builder.build());
                        break;
                    }
                    case Consts.RPCSET_VIPEXP: {
                        result = blockingService.setVipExp(controller, builder.build());
                        break;
                    }
                    case Consts.RPCSET_LEVEL: {
                        result = blockingService.setLevel(controller, builder.build());
                    }
                }
            }
            controller.reset();

            if (result != null) {
                return result.getValue();
            }
        } catch (ServiceException ex) {
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, "rpcSet error : " + StackTraceUtil.getStackTrace(ex));
        } finally{
            if (channel != null) {
                channel.close();
            }
        }
        return false;
    }

    // 注意： 该方法会在HttpServerHandler中调用，不是主线程（不是主线程访问该方法时必须确定主线程不会在此时关闭服务，因此需要与shutdown方法一起用synchronized）
    public synchronized List<Integer> getOnlinePlayerIds() {
        NettyRpcClient client = new NettyRpcClient(
                new NioClientSocketChannelFactory(
                Executors.newCachedThreadPool(),
                Executors.newCachedThreadPool()));

        NettyRpcChannel channel = client.blockingConnect(new InetSocketAddress(serverConfig.host, serverConfig.rpcPort));

        BlockingInterface blockingService = ManagerControlService.newBlockingStub(channel);

        // Get a new RpcController to use for this rpc call
        final RpcController controller = channel.newRpcController();

        // Create the request
        VoidProto.Builder voidProtoBuilder = VoidProto.newBuilder();

        // Now try a blocking RPC request
        IntValuesProto result = null;
        try {
            if (channel.isConnected()) {
                result = blockingService.getOnlinePlayerIds(controller, voidProtoBuilder.build());
            }
        } catch (ServiceException ex) {
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, "Get online player ids error : " + StackTraceUtil.getStackTrace(ex));
        } finally{
            if (channel != null) {
                channel.close();
            }
        }

        controller.reset();

        if (result != null) {
            return result.getValuesList();
        }

        return null;
    }

    public boolean changePlayerName(int cid, String playerName) {
        NettyRpcClient client = new NettyRpcClient(
                new NioClientSocketChannelFactory(
                Executors.newCachedThreadPool(),
                Executors.newCachedThreadPool()));

        NettyRpcChannel channel = client.blockingConnect(new InetSocketAddress(serverConfig.host, serverConfig.rpcPort));

        BlockingInterface blockingService = ManagerControlService.newBlockingStub(channel);

        // Get a new RpcController to use for this rpc call
        final RpcController controller = channel.newRpcController();

        IntStringProto.Builder builder = IntStringProto.newBuilder();
        builder.setIntValue(cid);
        builder.setStringValue(playerName);
        // Now try a blocking RPC request
        BoolValueProto result = null;
        try {
            if (channel.isConnected()) {
                result = blockingService.changePlayerName(controller, builder.build());
            }
        } catch (ServiceException ex) {
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, "change player name error : " + StackTraceUtil.getStackTrace(ex));
        } finally{
            if (channel != null) {
                channel.close();
            }
        }

        controller.reset();

        if (result != null) {
            return result.getValue();
        }

        return false;
    }
    
    public String consumerLog(int cid, int chargeType, long beginTime, long endTime) {
        NettyRpcClient client = new NettyRpcClient(
                new NioClientSocketChannelFactory(
                Executors.newCachedThreadPool(),
                Executors.newCachedThreadPool()));

        NettyRpcChannel channel = client.blockingConnect(new InetSocketAddress(serverConfig.host, serverConfig.rpcPort));

        BlockingInterface blockingService = ManagerControlService.newBlockingStub(channel);

        // Get a new RpcController to use for this rpc call
        final RpcController controller = channel.newRpcController();

        GMConsumerLogProto.Builder builder = GMConsumerLogProto.newBuilder();
        builder.setCid(cid);
        builder.setChargeType(chargeType);
        builder.setBeginTime(beginTime);
        builder.setEndTime(endTime);
        // Now try a blocking RPC request
        StringValueProto result = null;
        try {
            if (channel.isConnected()) {
                result = blockingService.consumerLog(controller, builder.build());
            }
        } catch (ServiceException ex) {
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, "get consumer log error : " + StackTraceUtil.getStackTrace(ex));
        } finally {
            if (channel != null) {
                channel.close();
            }
        }

        controller.reset();

        if (result != null) {
            return result.getValue();
        }

        return "";
    }

    public boolean addMail(int targetId, MailContentProto mailContentProto) {
        NettyRpcClient client = new NettyRpcClient(
                new NioClientSocketChannelFactory(
                Executors.newCachedThreadPool(),
                Executors.newCachedThreadPool()));

        NettyRpcChannel channel = client.blockingConnect(new InetSocketAddress(serverConfig.host, serverConfig.rpcPort));

        BlockingInterface blockingService = ManagerControlService.newBlockingStub(channel);

        // Get a new RpcController to use for this rpc call
        final RpcController controller = channel.newRpcController();

        GMMailProto.Builder builder = GMMailProto.newBuilder();
        builder.setTargetId(targetId);
        builder.setContent(mailContentProto);

        // Now try a blocking RPC request
        BoolValueProto result = null;
        try {
            if (channel.isConnected()) {
                result = blockingService.addMail(controller, builder.build());
            }
        } catch (ServiceException ex) {
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, "add mail error : " + StackTraceUtil.getStackTrace(ex));
        } finally{
            if (channel != null) {
                channel.close();
            }
        }

        controller.reset();

        if (result != null) {
            return result.getValue();
        }

        return false;
    }

    public boolean addNormalActivity(String data) {
        NettyRpcClient client = new NettyRpcClient(
                new NioClientSocketChannelFactory(
                Executors.newCachedThreadPool(),
                Executors.newCachedThreadPool()));
        NettyRpcChannel channel = client.blockingConnect(new InetSocketAddress(serverConfig.host, serverConfig.rpcPort));

        BlockingInterface blockingService = ManagerControlService.newBlockingStub(channel);

        // Get a new RpcController to use for this rpc call
        final RpcController controller = channel.newRpcController();

        StringValueProto.Builder builder = StringValueProto.newBuilder();
        builder.setValue(data);

        // Now try a blocking RPC request
        BoolValueProto result = null;
        try {
            if (channel.isConnected()) {
                result = blockingService.addNormalActivity(controller, builder.build());
            }
        } catch (ServiceException ex) {
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, "add normal activity error : " + StackTraceUtil.getStackTrace(ex));
        } finally{
            if (channel != null) {
                channel.close();
            }
        }

        controller.reset();

        if (result != null) {
            return result.getValue();
        }

        return false;
    }

    public boolean deleteNormalActivity(int activityId) {
        NettyRpcClient client = new NettyRpcClient(
                new NioClientSocketChannelFactory(
                Executors.newCachedThreadPool(),
                Executors.newCachedThreadPool()));
        NettyRpcChannel channel = client.blockingConnect(new InetSocketAddress(serverConfig.host, serverConfig.rpcPort));

        BlockingInterface blockingService = ManagerControlService.newBlockingStub(channel);

        // Get a new RpcController to use for this rpc call
        final RpcController controller = channel.newRpcController();

        IntValueProto.Builder builder = IntValueProto.newBuilder();
        builder.setValue(activityId);

        // Now try a blocking RPC request
        BoolValueProto result = null;
        try {
            if (channel.isConnected()) {
                result = blockingService.deleteNormalActivity(controller, builder.build());
            }
        } catch (ServiceException ex) {
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, "delete normal activity error : " + StackTraceUtil.getStackTrace(ex));
        } finally{
            if (channel != null) {
                channel.close();
            }
        }

        controller.reset();

        if (result != null) {
            return result.getValue();
        }

        return false;
    }

    public boolean setCardDrawActivity(String data) {
        NettyRpcClient client = new NettyRpcClient(
                new NioClientSocketChannelFactory(
                Executors.newCachedThreadPool(),
                Executors.newCachedThreadPool()));
        NettyRpcChannel channel = client.blockingConnect(new InetSocketAddress(serverConfig.host, serverConfig.rpcPort));

        BlockingInterface blockingService = ManagerControlService.newBlockingStub(channel);

        // Get a new RpcController to use for this rpc call
        final RpcController controller = channel.newRpcController();

        StringValueProto.Builder builder = StringValueProto.newBuilder();
        builder.setValue(data);

        // Now try a blocking RPC request
        BoolValueProto result = null;
        try {
            if (channel.isConnected()) {
                result = blockingService.setCardDrawActivity(controller, builder.build());
            }
        } catch (ServiceException ex) {
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, "set card draw activity error : " + StackTraceUtil.getStackTrace(ex));
        } finally{
            if (channel != null) {
                channel.close();
            }
        }

        controller.reset();

        if (result != null) {
            return result.getValue();
        }

        return false;
    }

    public boolean setStageActivity(String data) {
        NettyRpcClient client = new NettyRpcClient(
                new NioClientSocketChannelFactory(
                Executors.newCachedThreadPool(),
                Executors.newCachedThreadPool()));
        NettyRpcChannel channel = client.blockingConnect(new InetSocketAddress(serverConfig.host, serverConfig.rpcPort));

        BlockingInterface blockingService = ManagerControlService.newBlockingStub(channel);

        // Get a new RpcController to use for this rpc call
        final RpcController controller = channel.newRpcController();

        StringValueProto.Builder builder = StringValueProto.newBuilder();
        builder.setValue(data);

        // Now try a blocking RPC request
        BoolValueProto result = null;
        try {
            if (channel.isConnected()) {
                result = blockingService.setStageActivity(controller, builder.build());
            }
        } catch (ServiceException ex) {
            MLogger.getlogger().debuglog(LogConsts.LOGLEVEL_ERROR, "set stage activity error : " + StackTraceUtil.getStackTrace(ex));
        } finally{
            if (channel != null) {
                channel.close();
            }
        }

        controller.reset();

        if (result != null) {
            return result.getValue();
        }

        return false;
    }
}
